package com.itita.ww2.game.user;

import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import com.itita.ww2.core.R;
import com.itita.ww2.core.ServerService;
import com.itita.ww2.enums.AreaEnum;
import com.itita.ww2.game.user.event.UserEventHandler;
import com.itita.ww2.model.area.BaseArea;
import com.itita.ww2.model.enums.UserWMDataFields;
import com.itita.ww2.model.user.UserProfile;
import com.itita.ww2.redis.RedisUtil;
import com.itita.ww2.redis.cache.Table;
import com.itita.ww2.redis.core.Redis;
import com.itita.ww2.utils.properties.ServerProperties;
import com.lambdaworks.redis.KeyValue;
import com.lambdaworks.redis.ScoredValue;
import com.smartfoxserver.v2.db.SFSDBManager;
import com.smartfoxserver.v2.entities.User;
import com.smartfoxserver.v2.entities.data.ISFSArray;
import com.smartfoxserver.v2.entities.data.ISFSObject;
import com.smartfoxserver.v2.entities.data.SFSArray;
import com.smartfoxserver.v2.entities.data.SFSObject;
import com.smartfoxserver.v2.extensions.SFSExtension;
import com.smartfoxserver.v2.util.MD5;

/**
 * 用户数据管理器
 * @author NorthLan
 *
 */
public class UserService {
	private static final Logger logger = LoggerFactory.getLogger(UserService.class);
	private static UserService instance;

	public static UserService getInstance() {
		if (instance == null) {
			instance = new UserService();
		}
		return instance;
	}

	/*
	 * fields
	 */
	private LoadingCache<Integer, UserProfile> userCache;

	/*
	 * initialized
	 */
	public void init() {
		initRedis();
		initUserCache();
		// 注册事件
		UserEventHandler.registerUserEvents(this);
	}

	private void initRedis() {
		Redis redis = ServerService.getInstance().getRedis();
		// setTable
		String[] keyName = RedisUtil.genericKey("uid");
		redis.setTable(R.ServerRedisKey.USER_GAMEDATA, keyName, false, true).init();
		// with sort
		redis.setTable(R.ServerRedisKey.USER_WMDATA, keyName, false, true)
						.withSort("base_occTimes", "base_defTimes",	"base_count").init();
		
		redis.setTable(R.ServerRedisKey.USER_MISSIONDATA, keyName, false, true).init();
	}

	private void initUserCache() {
		// @formatter:off
		this.userCache = CacheBuilder.newBuilder()
				.maximumSize(ServerProperties.getInt("user_cache_max_size", 2000))
				.expireAfterAccess(ServerProperties.getInt("user_cache_expire_after_access", 2000), TimeUnit.MINUTES)
				.expireAfterWrite(ServerProperties.getInt("user_cache_expire_after_write", 2000), TimeUnit.MINUTES)
				.initialCapacity(100)
				.recordStats()
				.removalListener(new RemovalListener<Integer, UserProfile>() {

					@Override
					public void onRemoval(RemovalNotification<Integer, UserProfile> arg0) {
						logger.info("user profile has removed. uid:[{}], name:[{}]", arg0.getKey(),
								arg0.getValue().getNickname());
					}

				}).build(new CacheLoader<Integer, UserProfile>() {

					@Override
					public UserProfile load(Integer arg0) throws Exception {
						return UserProfile.handleLazy(arg0);
					}
				});
		// @formatter:on
	}

	///////////////////////////////////////////////////////////////////////////
	public Table getCacheUserDataTable() {
		return ServerService.getInstance().getRedis().getTable(R.ServerRedisKey.USER_GAMEDATA);
	}

	//	public Table getCacheAggrUserDataTable() {
	//		return ServerService.getInstance().getRedis().getTable(R.ServerRedisKey.USER_AGGRDATA);
	//	}

	public Table getCacheWMUserDataTable() {
		return ServerService.getInstance().getRedis().getTable(R.ServerRedisKey.USER_WMDATA);
	}

	public Table getCacheMissionDataTable() {
		return ServerService.getInstance().getRedis().getTable(R.ServerRedisKey.USER_MISSIONDATA);
	}
	///////////////////////////////////////////////////////////////////////////

	public List<User> getAllUser() {
		SFSExtension ext = ServerService.getInstance().getMainExtension();
		return ext.getParentZone().getUserManager().getAllUsers();
	}

	public UserProfile getUserProfile(User user) {
		return getUserProfile(getUid(user));
	}

	public UserProfile getUserProfile(int uid) {
		if (uid == -1) {
			return null;
		}
		UserProfile userProfile = null;
		try {
			userProfile = userCache.get(uid);
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return userProfile;
	}

	/**
	 * 获取所有的userProfile(包括未加载的)<br>
	 * 未加载的将延迟加载
	 */
	public List<UserProfile> getAllUserProfile(Iterable<Integer> ids) {
		try {
			return this.userCache.getAll(ids).values().asList();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}
		return null;
	}

	public Collection<UserProfile> getAllUserProfile() {
		return this.userCache.asMap().values();
	}

	public void handleUserJoin(UserProfile profile) {
		userCache.put(profile.getUid(), profile);
	}

	/**
	 * 获取在线用户的uid
	 */
	public int getUid(User user) {
		return user.containsProperty("uid") ? (int) user.getProperty("uid") : -1;
	}

	/**
	 * 获取某区域的玩家列表(精确)
	 */
	public List<User> getAreaUsers(BaseArea area) {
		return userCache.asMap().values().stream().filter(p -> p.getCurArea().equals(area)).map(k -> k.getSFSUser())
				.collect(Collectors.toList());
	}

	public List<UserProfile> getAreaUsersProfile(BaseArea area) {
		return userCache.asMap().values().stream().filter(p -> p.getCurArea().equals(area))
				.collect(Collectors.toList());
	}

	/**
	 * 获取某区域的玩家列表(模糊)
	 */
	public List<User> getAreaUsers(AreaEnum... areas) {
		return userCache.asMap().values().stream()
				.filter(p -> Arrays.asList(areas).contains(p.getCurArea().getAreaEnum())).map(k -> k.getSFSUser())
				.collect(Collectors.toList());
	}

	public ISFSObject getUserData(int uid) {
		SFSDBManager dbManager = ServerService.getInstance().getSFSDBManager();
		try {
			String sql = "SELECT * FROM `users` WHERE id = " + uid;
			ISFSArray arr = dbManager.executeQuery(sql);
			if (arr.size() > 0) {
				return arr.getSFSObject(0);
			}
		} catch (SQLException e) {
			e.printStackTrace();
			return null;
		}
		return null;
	}

	/**
	 * 渠道帐号是否绑定过游戏帐号
	 */
	public boolean isBound(String channelId) {
		SFSDBManager dbManager = ServerService.getInstance().getSFSDBManager();
		try {
			String sql = "SELECT * FROM `users` WHERE `channelId` = ?";
			ISFSArray arr = dbManager.executeQuery(sql, Arrays.asList(channelId).toArray());
			if (arr.size() > 0) {
				return true;
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 获取绑定的帐号信息(用户名+密码)
	 */
	public ISFSObject getBoundAccount(String channelId) {
		ISFSObject ret = SFSObject.newInstance();
		SFSDBManager dbManager = ServerService.getInstance().getSFSDBManager();
		try {
			String sql = "SELECT * FROM `users` WHERE `channelId` = ?";
			ISFSArray arr = dbManager.executeQuery(sql, Arrays.asList(channelId).toArray());
			if (arr.size() > 0) {
				ISFSObject obj = arr.getSFSObject(0);
				String username = obj.getUtfString("username");
				String password = MD5.getInstance().getHash(username + ServerProperties.getString("my.salt", "_nl"));
				ret.putUtfString("username", username);
				ret.putUtfString("password", password);
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return ret;
	}

	/**
	 * 绑定帐号
	 */
	public boolean bind(int uid, int channel, String channelId) {
		SFSDBManager dbManager = ServerService.getInstance().getSFSDBManager();
		try {
			String sql = "UPDATE `users` SET `channel` = ?,`channelId` = ? WHERE `id` = ?";
			dbManager.executeUpdate(sql, Arrays.asList(channel, channelId, uid).toArray());
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return false;
	}

	/**
	 * 获取玩家排行
	 */
	public ISFSArray getAllUserRanking(UserWMDataFields field, int start, int count) {
		ISFSArray ref = SFSArray.newInstance();
		List<ScoredValue<String>> result = getCacheWMUserDataTable().sort(field.getValue(), start, count, true);
		int ranking = 1;
		for (ScoredValue<String> data : result) {
			ISFSObject obj = SFSObject.newInstance();
			obj.putInt("count", (int) data.score);
			obj.putInt("ranking", ranking++);
			int uid = Integer.valueOf(data.value);

			UserProfile userProfile = UserService.getInstance().getUserProfile(uid);

			obj.putInt("uid", uid);
			obj.putUtfString("uName", userProfile.getNickname());
			obj.putUtfString("country", userProfile.getCountry());

			ref.addSFSObject(obj);
		}
		return ref;
	}

	/**
	 * 获取玩家排名信息
	 * @param uid 玩家uid
	 * @param field 排序的列
	 */
	public ISFSObject getUserRank(int uid, UserWMDataFields field) {
		ISFSObject ref = SFSObject.newInstance();
		KeyValue<Integer, Double> result = getCacheWMUserDataTable().rank(field.getValue(), RedisUtil.genericKey(uid),
				true);
		if (result != null) {
			ref.putInt("ranking", result.key);
			ref.putInt("count", result.value.intValue());
		}
		return ref;
	}
}
